package com.dh.spring.client.bootstrap;

import com.dh.spring.client.handler.ChannelHandler;
import com.dh.spring.client.file.common.ReadEvent;
import com.dh.spring.client.file.common.SendEntity;
import com.dh.spring.client.file.common.CommonReadEventHandler;
import com.dh.spring.client.file.download.SocketChannelDownloadHandler;
import com.dh.spring.client.file.upload.SocketChannelUploadHandler;
import com.dh.spring.client.http.FileExecStatus;
import com.dh.spring.common.BasicConstant;
import com.dh.spring.util.LocalTime;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import java.time.LocalDateTime;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.*;

/**
 * TODO 处理文件操作的handler(上传或下载)
 * */

@Slf4j
@SuppressWarnings("all")
public class FileCoreServiceHandler implements ChannelHandler {
    /**
     * //TODO [channelName <------> Runnable] 通道名称 对应的通道就绪处理器线程
     * */
    public static Map<String, Runnable> specificChannelRunnableMaps = new ConcurrentHashMap<>();
    /**
     * //TODO [channelName <------> Runnable] 通道名称 通道公共就绪读事件处理器线程
     * */
    public static Map<String, Callable> commonChannelRunnableMaps = new ConcurrentHashMap<>();

    public FileCoreServiceHandler() {

    }

    @Override
    public void handler(SelectionKey selectionKey) {
        if(selectionKey.isValid() && selectionKey.isConnectable()) {
            connectedHandler(selectionKey);
        } else if(selectionKey.isReadable()) {
            readHandler(selectionKey);
        } else if(selectionKey.isWritable()) {
            writeHandler(selectionKey);
        }
    }

    private void connectedHandler(SelectionKey selectionKey) {
        SocketChannel socketChannel = null;
        try {
            socketChannel = (SocketChannel) selectionKey.channel();
            /**
             * TODO 连接请求已发起，循环等待与服务端socket三次握手成功建立，每应答一次，都在序号基础上加1进行应答，
             *  客户端应答服务端则将服务端发送的序号加1进行回应 服务端应答客户端则将客户端发送的序号加1进行回应，即
             *  每次应答都是执行加1操作进行，四次挥手断开连接同样，所以不论是客户端还是服务端只在应答时序号完成加1
             *  操作并且成功发送对端，对端真正接受到才算一次请求成功；三次握手如下
             *      如例：客户端请求服务端：
             *                                      SYN    Seq=LSN(c)
             *          Client ---------------------------------------------------------->  Server
             *                          SYN     Seq=LSN(s)   ACK=LSN(c)+1
             *          client <----------------------------------------------------------  Server
             *                          ACK     ACK=LSN(s)+1
             *          Client ----------------------------------------------------------> Server
             *      服务端发送序号[ACK=LSN(c)+1]对客户端连接请求应答，并发送Seq=LSN(s)用于客户端同意服务端的请求
             *      后并在此序号Seq=LSN(s)上加1进行对服务端请求的应答，即客户端应答数据必须为服务端序号LSN(s)+1
             * */
            while (true) {if(socketChannel.finishConnect()) {break;}}
            log.info("[" + LocalTime.formatDate(LocalDateTime.now()) + "] FileCoreServiceHandler | --> 与文件服务端连接成功,开始订阅写事件发送文件校验信息到服务端(用于判断文件操作在文件服务端的状态(是否首次上传或是重复上传或是首次下载还是下载的文件不存在)): connected = success, client = {}, server = {}", ((SocketChannel) selectionKey.channel()).getLocalAddress(), ((SocketChannel) selectionKey.channel()).getRemoteAddress());
            //TODO 连接完成后通过对当前SelectionKey注册OP_READ读事件和OP_WRITE写事件,注册写事件用于执行与文件服务端建立连接成功后发送文件数据
            selectionKey.interestOps(selectionKey.interestOps() | SelectionKey.OP_WRITE);
            selectionKey.interestOps(selectionKey.interestOps() & ~SelectionKey.OP_CONNECT); //TODO 连接成功后取消连接成功事件
        } catch (IOException e) {
            FileCoreServiceHandler.closeSocketChannel(selectionKey, null, "FileCoreServiceHandler","文件客户端与文件服务端连接异常, error = " + e.getMessage(), ((SendEntity) selectionKey.attachment()).getExec(), ((SendEntity) selectionKey.attachment()).getFileName(), 0);
        }
    }

    private void writeHandler(SelectionKey selectionKey) {
        if(((SendEntity) selectionKey.attachment()).getExec().equals(BasicConstant.UPLOAD))
            uploadFileSendHandler(selectionKey);

        if(((SendEntity) selectionKey.attachment()).getExec().equals(BasicConstant.DOWNLOAD))
            downloadFileSendHandler(selectionKey);

        if(((SendEntity) selectionKey.attachment()).getExec().equals(BasicConstant.LINUX_COMMAND))
            linuxCommandSendHandler(selectionKey);
    }

    //TODO 文件上传写事件发送
    private void uploadFileSendHandler(SelectionKey selectionKey) {
        /**
         * 文件内容信息发送：1、发送文件基本数据：先向服务器发送文件名称长度数据、文件大小以及文件名称字节数据到服务器后再读事件中接收文件上传处理响应，判断是否可上传;
         *               2、 接收文件基本数据处理响应:
         * */
        try {
            selectionKey.interestOps(selectionKey.interestOps() & ~SelectionKey.OP_WRITE);
            //TODO 获取当前选择键需要发送的数据对象
            ByteBuffer byteBuffer = null;
            String fileName = "";
            while(true) {
                byteBuffer = ((SendEntity) selectionKey.attachment()).getByteBuffer();
                byteBuffer.flip();
                if(!byteBuffer.hasRemaining()){
                    TimeUnit.MILLISECONDS.sleep(10);
                    continue;
                }
                break;
            }

            int failedSendCount = 0;
            while(byteBuffer.hasRemaining()) {
                int sentBytes = ((SocketChannel) selectionKey.channel()).write(byteBuffer);

                if(0 == sentBytes) {
                    if(failedSendCount == 5) {
                        selectionKey.interestOps(selectionKey.interestOps() & ~SelectionKey.OP_WRITE);
                        selectionKey.selector().selectNow();
                        break;
                    }

                    failedSendCount++;
                }
            }

            if(failedSendCount == 5) {
                selectionKey.interestOps(selectionKey.interestOps() | SelectionKey.OP_WRITE);
            }

            //TODO 向服务端发送数据重置上传文件启动，此处直接触发文件上传线程上传文件，无需订阅读事件，因为上传文件为客户端发起，客户端向服务端发送数据，服务端没有任何数据向客户端发送
            //TODO 所以订阅了读事件会导致什么也不触发
            if(specificChannelRunnableMaps.isEmpty()) {
                log.info("[" + LocalTime.formatDate(LocalDateTime.now()) + "] FileCoreServiceHandler | --> 向文件服务端发送文件信息成功,待文件服务端针对文件上传进行校验后返回,并订阅读事件接收文件服务端的上传校验结果返回: fileName = {}, exec = {}", ((SendEntity) selectionKey.attachment()).getFileName(), ((SendEntity) selectionKey.attachment()).getExec());
                selectionKey.interestOps(selectionKey.interestOps() | SelectionKey.OP_READ);
                return;
            }
            SocketChannelUploadHandler socketChannelUploadHandler = (SocketChannelUploadHandler) specificChannelRunnableMaps.get(((SendEntity) selectionKey.attachment()).getChannelName());
            if (null != socketChannelUploadHandler) {
                socketChannelUploadHandler.flag = true;//TODO 设置可继续读就绪标记
                selectionKey.interestOps(selectionKey.interestOps() & ~SelectionKey.OP_READ);
            } else {
                log.info("[" + LocalTime.formatDate(LocalDateTime.now()) + "] FileCoreServiceHandler | --> 向文件服务端发送文件信息成功,待文件服务端针对文件上传进行校验后返回,并订阅读事件接收文件服务端的上传校验结果返回: fileName = {}, exec = {}", ((SendEntity) selectionKey.attachment()).getFileName(), ((SendEntity) selectionKey.attachment()).getExec());
                selectionKey.interestOps(selectionKey.interestOps() | SelectionKey.OP_READ);
                return;
            }
        } catch (Exception e) {
            log.error("[" + LocalTime.formatDate(LocalDateTime.now()) + "] FileCoreServiceHandler | --> 文件操作校验数据向文件服务端发送异常, error = {}", e.getMessage());
            selectionKey.interestOps(selectionKey.interestOps() & ~SelectionKey.OP_WRITE);
            selectionKey.cancel();
            if(null != selectionKey.channel()) {
                try {
                    selectionKey.channel().close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
        }
    }

    //TODO 文件下载写事件发送
    private void downloadFileSendHandler(SelectionKey selectionKey) {
        try {
            //TODo 如果网络正常，且write方法发送数据成功，则取消当前可选键的写事件注册终止选择键的写事件触发
            selectionKey.interestOps(selectionKey.interestOps() & ~SelectionKey.OP_WRITE);

            //TODO 获取当前选择键需要发送的数据对象
            ByteBuffer byteBuffer = null;
            String fileName = "";
            while(true) {
                byteBuffer = ((SendEntity) selectionKey.attachment()).getByteBuffer();
                byteBuffer.flip();
                if(!byteBuffer.hasRemaining()){
                    TimeUnit.MILLISECONDS.sleep(10);
                    continue;
                }
                break;
            }

            /**
             * 如果当前客户端SocketChannel发送缓冲区一直过满导致应用数据一直无法发送到缓冲区，此时byteBuffer中一直存在数据，
             * 会导致while死循环，且write()方法会一直返回为0，表示一直写不进发送缓冲区，此时有可能为客户端网络不稳定或是网络
             * 状态不好，导致内核发送缓冲区的数据发送出去过慢，短时间内发送缓冲区一直过满，write()方法一直写入不成功
             * */
            int failedSendCount = 0;
            while(byteBuffer.hasRemaining()) {
                int sentBytes = ((SocketChannel) selectionKey.channel()).write(byteBuffer);

                if(0 == sentBytes) {
                    //TODO 当累积write调用成功写入的字节数为0的次数达到failedSendCount次数(5)将终止该循环，取消当前可选键写事件的处理
                    //TODO 注册写事件后，能否触发写事件与当前注册写事件的SocketChannel的写缓冲区是否已满，满的话无法写入
                    if(failedSendCount == 5) {
                        //TODO 取消注册的写事件
                        selectionKey.interestOps(selectionKey.interestOps() & ~SelectionKey.OP_WRITE);
                        selectionKey.selector().selectNow();//TODO selectNow()方法只调用一次，不会阻塞
                        break;
                    }

                    failedSendCount++;
                }
            }

            if(failedSendCount == 5) {
                //TODO 重新注册写事件，直到写缓冲有空间可写如数据
                selectionKey.interestOps(selectionKey.interestOps() | SelectionKey.OP_WRITE);
            }

            log.info("[" + LocalTime.formatDate(LocalDateTime.now()) + "] FileCoreServiceHandler | --> 向文件服务端发送文件信息成功,待文件服务端针对文件下载进行校验后返回,并订阅读事件接收文件服务端的下载校验结果返回: fileName = {}, exec = {}", ((SendEntity) selectionKey.attachment()).getFileName(), ((SendEntity) selectionKey.attachment()).getExec());
            selectionKey.interestOps(selectionKey.interestOps() | SelectionKey.OP_READ);
        } catch (Exception e) {
            log.error("[" + LocalTime.formatDate(LocalDateTime.now()) + "] FileCoreServiceHandler | --> 文件下载校验数据发送文件服务器异常, error = {}", e.getMessage());
            selectionKey.interestOps(selectionKey.interestOps() & ~SelectionKey.OP_WRITE);
            selectionKey.cancel();
            if(null != selectionKey.channel()) {
                try {
                    selectionKey.channel().close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
        }
    }

    //TODO linux命令写事件发送
    private void linuxCommandSendHandler(SelectionKey selectionKey) {
        try {
            selectionKey.interestOps(selectionKey.interestOps() & ~SelectionKey.OP_WRITE);

            ByteBuffer byteBuffer = null;
            String fileName = "";
            while(true) {
                byteBuffer = ((SendEntity) selectionKey.attachment()).getByteBuffer();
                byteBuffer.flip();
                if(!byteBuffer.hasRemaining()){
                    TimeUnit.MILLISECONDS.sleep(10);
                    continue;
                }
                break;
            }

            int failedSendCount = 0;
            while(byteBuffer.hasRemaining()) {
                int sentBytes = ((SocketChannel) selectionKey.channel()).write(byteBuffer);
                if(0 == sentBytes) {
                    if(failedSendCount == 5) {
                        //TODO 取消注册的写事件
                        selectionKey.interestOps(selectionKey.interestOps() & ~SelectionKey.OP_WRITE);
                        selectionKey.selector().selectNow();//TODO selectNow()方法只调用一次，不会阻塞
                        break;
                    }

                    failedSendCount++;
                }
            }

            if(failedSendCount == 5) {
                selectionKey.interestOps(selectionKey.interestOps() | SelectionKey.OP_WRITE);
            }

            log.info("[" + LocalTime.formatDate(LocalDateTime.now()) + "] FileCoreServiceHandler | --> 向文件服务端发送linux命令成功,待文件服务端针对命令进行校验后返回,并订阅读事件接收文件服务端的执行命令校验结果返回: linuxCommand = {}", ((SendEntity) selectionKey.attachment()).getExec() + GlobalNIOClientListenerDelegate.SOCKET_REMOTE_PORT);
            selectionKey.interestOps(selectionKey.interestOps() | SelectionKey.OP_READ);
        } catch (Exception e) {
            log.error("[" + LocalTime.formatDate(LocalDateTime.now()) + "] FileCoreServiceHandler | --> linux命令校验数据发送文件服务器异常, linuxCommand = {}, error = {}", ((SendEntity) selectionKey.attachment()).getExec() + GlobalNIOClientListenerDelegate.SOCKET_REMOTE_PORT, e.getMessage());
            selectionKey.interestOps(selectionKey.interestOps() & ~SelectionKey.OP_WRITE);
            selectionKey.cancel();
            if(null != selectionKey.channel()) {
                try {
                    selectionKey.channel().close();
                } catch (IOException e1) {
                    e1.printStackTrace();
                }
            }
        }
    }

    private void readHandler(SelectionKey selectionKey) {
        try {
            if(((SendEntity) selectionKey.attachment()).getExec().equals(BasicConstant.LINUX_COMMAND))
                readLinuxCommandHandler(selectionKey);
            if(((SendEntity) selectionKey.attachment()).getExec().equals(BasicConstant.UPLOAD) || ((SendEntity) selectionKey.attachment()).getExec().equals(BasicConstant.DOWNLOAD))
                readConcretHandler(selectionKey);
        } catch (Exception e) {
            FileCoreServiceHandler.closeSocketChannel(selectionKey, null, "FileCoreServiceHandler","读取文件信息异常, error = " + e.getMessage(), ((SendEntity) selectionKey.attachment()).getExec().endsWith(BasicConstant.UPLOAD)?BasicConstant.UPLOAD:BasicConstant.DOWNLOAD, ((SendEntity) selectionKey.attachment()).getFileName(), 0);
        }
    }

    /**
     *  进度：服务端已完成，现在需要处理客户端处理完服务端回传的文件反馈信息需要向服务端发送1个字节的命令，用于通知服务端开始发送文件数据
     * */
    private void readConcretHandler(SelectionKey selectionKey) throws ExecutionException, InterruptedException {
        //TODO map为空，为首次执行读事件处理，直接开辟线程，不为空，则为已有线程的SocketChannel就绪继续处理
        if(commonChannelRunnableMaps.isEmpty()) {
            CommonReadEventHandler commonReadEventHandler = new CommonReadEventHandler(selectionKey);
            commonChannelRunnableMaps.put(((SendEntity) selectionKey.attachment()).getChannelName(), commonReadEventHandler);

            //TODO --> 开辟线程校验文件信息并读取文件服务端反馈
            Future<ReadEvent> future = GlobalNIOClientBootstrap.executorService.submit(commonReadEventHandler);
            try {
                String result = "";
                if((null != (result = future.get().getResult())) && !"".equals(result)) {
                    if(result.equals("[FAIL]")) {
                        return;
                    } else {
                        ByteBuffer byteBuffer = ((SendEntity) selectionKey.attachment()).getByteBuffer();
                        byteBuffer.clear();
                        byteBuffer.putInt(1);
                        log.info("[" + LocalTime.formatDate(LocalDateTime.now()) + "] FileCoreServiceHandler | --> 向文件服务端发送int(4字节)文件操作命令，告知文件服务端准备开始接收文件客户端发送的文件流数据: fileName = {}, exec = {}", ((SendEntity) selectionKey.attachment()).getFileName(), ((SendEntity) selectionKey.attachment()).getExec());
                        selectionKey.interestOps(selectionKey.interestOps() | SelectionKey.OP_WRITE).attach(selectionKey.attachment());
                        GlobalNIOClientBootstrap.executorService.submit(FileCoreServiceHandler.specificChannelRunnableMaps.get(((SendEntity) selectionKey.attachment()).getChannelName()));
                    }
                }
            } catch (InterruptedException e) {
                e.printStackTrace();
            } catch (ExecutionException e) {
                e.printStackTrace();
            }
            return;
        } else {
            CommonReadEventHandler commonReadEventHandler = (CommonReadEventHandler) commonChannelRunnableMaps.get(((SendEntity) selectionKey.attachment()).getChannelName());
            if(null != commonReadEventHandler) { //TODO 表明当前通道对应的读事件公共线程还未处理完，则继续执行读取操作，直到文件基础数据读完并从集合删除
                if(!"[OK]".equals(commonReadEventHandler.getReadEvent().getResult())) {
                    FileCoreServiceHandler.closeSocketChannel(selectionKey, null, "FileCoreServiceHandler","读取文件信息异常", ((SendEntity) selectionKey.attachment()).getExec().endsWith(BasicConstant.UPLOAD)?BasicConstant.UPLOAD:BasicConstant.DOWNLOAD, ((SendEntity) selectionKey.attachment()).getFileName(), 0);
                    return;
                }
            }else { //TODO --> 此else用于处理多文件上传时，当客户端发来第二个文件的socket连接时需要重新创建对应的CommontReadEventHandler来处理对应文件的校验，因为上面if的逻辑一旦第一个文件
                //TODO 处理完成后，commonChannelRunnableMaps就不为空了，此时需要在else中创建可能多文件上传是第二个文件的校验handler创建
                commonReadEventHandler = new CommonReadEventHandler(selectionKey);
                commonChannelRunnableMaps.put(((SendEntity) selectionKey.attachment()).getChannelName(), commonReadEventHandler);
                this.synchronizeReadFileMetadata(selectionKey, commonReadEventHandler);
                return;
            }
        }

        if(((SendEntity) selectionKey.attachment()).getExec().equals(BasicConstant.UPLOAD)) {
            SocketChannelUploadHandler socketChannelUploadHandler = (SocketChannelUploadHandler) specificChannelRunnableMaps.get(((SendEntity) selectionKey.attachment()).getChannelName());
            if(null != socketChannelUploadHandler) {
                socketChannelUploadHandler.flag = true;//TODO 设置可继续读就绪标记
                selectionKey.interestOps(selectionKey.interestOps() & ~SelectionKey.OP_READ);
            }
            return;
        }

        if(((SendEntity) selectionKey.attachment()).getExec().equals(BasicConstant.DOWNLOAD)) {
            SocketChannelDownloadHandler socketChannelDownloadHandler = (SocketChannelDownloadHandler) specificChannelRunnableMaps.get(((SendEntity) selectionKey.attachment()).getChannelName());
            if(null != socketChannelDownloadHandler) {
                socketChannelDownloadHandler.flag = true;//TODO 设置可继续读就绪标记
                selectionKey.interestOps(selectionKey.interestOps() & ~SelectionKey.OP_READ);
            }
            return;
        }
    }

    private void readLinuxCommandHandler(SelectionKey selectionKey) {
        ByteBuffer byteBuffer = ByteBuffer.allocate(2 * 1024);
        int readEndEOF = -1;
        SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
        SendEntity sendEntity = ((SendEntity) selectionKey.attachment());
        StringBuffer stringBuffer = sendEntity.getStringBuffer();
        try {
            while ((readEndEOF = socketChannel.read(byteBuffer)) > 0) {
                byteBuffer.flip(); //TODO 切换读模式

                if (byteBuffer.hasRemaining()) {
                    byte[] bytes = new byte[byteBuffer.remaining()];
                    byteBuffer.get(bytes);
                    stringBuffer.append(new String(bytes));
                }
            }

            if(stringBuffer.toString().endsWith(BasicConstant.LINUX_COMMAND_EXECUTE_RESULT_END)) {
                //TODO --> 设置命令执行结果
                LinkedBlockingQueue linkedBlockingQueue = ServerSocketChannelHttpRequestHandler.fileExecStatusMap.get(BasicConstant.LINUX_COMMAND);
                if(linkedBlockingQueue.contains(sendEntity.getFileExecStatus())) {
                    Iterator iterator = linkedBlockingQueue.iterator();
                    FileExecStatus fileExecStatus = null;
                    while (iterator.hasNext())
                        if(( fileExecStatus = ((FileExecStatus) iterator.next())) == sendEntity.getFileExecStatus()) {
                            fileExecStatus.setResult(stringBuffer);
                            break;
                        }
                }

                selectionKey.interestOps(selectionKey.interestOps() & ~SelectionKey.OP_READ);
                closeSocketChannel(selectionKey, null, "FileCoreServiceHandler","linux命令执行结果读取完成,将取消通道读写事件并关闭通道", sendEntity.getExec() + GlobalNIOClientListenerDelegate.SOCKET_REMOTE_PORT, "NO FILE", 0);
            }
        } catch (Exception e) {
            closeSocketChannel(selectionKey, null, "FileCoreServiceHandler","读取文件服务端linux命令执行结果数据异常, error = " + e.getMessage(), BasicConstant.LINUX_COMMAND, "NO FILE", 0);
        }
    }

    //TODO --> 读取文件元数据信息
    private void synchronizeReadFileMetadata(SelectionKey selectionKey, CommonReadEventHandler commonReadEventHandler) throws ExecutionException, InterruptedException {
        //TODO --> 1、异步执行通过CommonReadEventHandler校验文件的合法性，并获取结果
        Future<ReadEvent> future = GlobalNIOClientBootstrap.executorService.submit(commonReadEventHandler);
        ReadEvent readEvent = null;
        if(null != (readEvent = future.get())) {
            //TODO 2、待处理的文件不能被正确处理，result结果值不为[OK]，关闭当前通道
            if(!"[OK]".equals(readEvent.getResult())) {
                log.info("[" + LocalTime.formatDate(LocalDateTime.now()) + "] FileCoreServiceHandler | --> 文件客户端执行本地文件操作前校验失败，result = {}", readEvent.getResult());
                FileCoreServiceHandler.closeSocketChannel(selectionKey, null, "FileCoreServiceHandler","文件校验失败", readEvent.getFileExec(), readEvent.getFileName(), 0);
                return;
            } else {
                log.info("[" + LocalTime.formatDate(LocalDateTime.now()) + "] FileCoreServiceHandler | --> 向文件服务端发送int(4字节)文件操作命令，告知文件服务端准备开始接收文件客户端发送的文件流数据: fileName = {}, exec = {}", ((SendEntity) selectionKey.attachment()).getFileName(), ((SendEntity) selectionKey.attachment()).getExec());
                ByteBuffer byteBuffer = ((SendEntity) selectionKey.attachment()).getByteBuffer();
                byteBuffer.clear();
                byteBuffer.putInt(1);
                ((SendEntity) selectionKey.attachment()).setFileName(readEvent.getFileName());
                selectionKey.interestOps(selectionKey.interestOps() | SelectionKey.OP_WRITE).attach(selectionKey.attachment());
                //TODO --> 启动文件上传或下载线程，执行文件的上传或下载，此线程真正执行上传或是下载需要上面int(4字节的命令)型的数据发送到服务端待服务端收到并解析后,文件服务端开启对应线程等待接收文件上传或下载流数据
                GlobalNIOClientBootstrap.executorService.submit(FileCoreServiceHandler.specificChannelRunnableMaps.get(((SendEntity) selectionKey.attachment()).getChannelName()));
            }
        }
    }

    //TODO 关闭SocketChannel
    public static void closeSocketChannel(SelectionKey selectionKey, FileChannel fileChannel, String className, String logMessage, String exec, String fileName, int writedBytes) {
        log.info("[" + LocalTime.formatDate(LocalDateTime.now()) + "] FileCoreServiceHandler | --> 文件客户端将关闭通道并释放资源: className = {}, closed reason = {}, fileName = {}, exec = {}", className, "".equals(logMessage)?"no reason":logMessage, "NO FILE".equals(fileName)?"NO FILE":fileName, exec);
        GlobalNIOClientBootstrap.fileChannelsMap.remove(selectionKey);
        FileCoreServiceHandler.specificChannelRunnableMaps.remove(((SendEntity) selectionKey.attachment()).getChannelName());
        FileCoreServiceHandler.commonChannelRunnableMaps.remove(((SendEntity) selectionKey.attachment()).getChannelName());
        SocketChannel socketChannel = (SocketChannel) selectionKey.channel();
        if(socketChannel.isConnected()) {
            try {
                String host = ((InetSocketAddress) socketChannel.getRemoteAddress()).getAddress().getHostAddress();
                int port = ((InetSocketAddress) socketChannel.getRemoteAddress()).getPort();
                selectionKey.cancel();
                socketChannel.close();
                log.info("[" + LocalTime.formatDate(LocalDateTime.now()) + "] {} | --> 文件客户端已关闭与文件服务端连接, address = {ip = {}, port = {}}",className, host, port);
            } catch (IOException e) {
                log.error("[" + LocalTime.formatDate(LocalDateTime.now()) + "] {} | --> socket closed exception = {}", e.getMessage());
            }
        }

        if (null != fileChannel) {
            try {
                fileChannel.close();
                log.info("[" + LocalTime.formatDate(LocalDateTime.now()) + "] {} | --> 文件操作结果: operated = success, fileName = {}, exec = {}, writeBytes = {}, 已关闭文件上传通道", className, fileName, exec, writedBytes);
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }
}
